/**
 * Copyright [2012-2013] [Guðmundur Björn Birkisson]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. Copyright [2012-2013] [Guðmundur Björn Birkisson]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * ---------------------------------------------------------------------------
 * 
 * Class name:  MainWin
 * 
 * Description of class:
 * 
 * This is the main window of the UI. It glues together the whole system.
 * 
 */

package karel;

import is.hi.cs.morpho.Channel;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.filechooser.FileNameExtensionFilter;
import karel.UI.Console;
import karel.UI.DebugWindow;
import karel.editors.BasicEditor;
import karel.editors.GeneralEditor;
import karel.editors.GeneralTextEditor;
import karel.editors.mapeditor.MapEditor;
import karel.gamelogic.GameContainer;
import karel.gamelogic.GameController;
import karel.gamelogic.StepController;
import karel.graphics.BufferedRenderer;
import karel.lang.RDParserException;
import karel.lang.RDParserWithBaseCode;
import karel.util.MapIO;
import karel.util.TextIO;

public class MainWin extends javax.swing.JFrame implements BasicEditor {

    private GeneralEditor ed;
    final JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
    private static final String KEY_MAINWIN_X = "mainwindow.x";
    private static final String KEY_MAINWIN_Y = "mainwindow.y";
    private static final String KEY_MAINWIN_WIDTH = "mainwindow.width";
    private static final String KEY_MAINWIN_HEIGHT = "mainwindow.height";
    private static final String KEY_MAINWIN_VIEWSPLITTER = "mainwindow.viewsplitter";
    private static final String KEY_MAINWIN_SPLITPANEL = "mainwindow.splitPanel";
    private static final String KEY_MAINWIN_GAMESPEED = "mainwindow.gameSpeed";
    private final Preferences prefs;
    private DebugWindow debug;
    public static File map;
    private File code;
    private Channel channel;
    private StringBuffer theOutput = new StringBuffer();
    private Thread kicker;
    private long lastFlush = 0;
    private int currentState;
    private MapEditor mapEd;
    private boolean defaultmap;

    /**
     * Constructor for the MainWindow of the system.
     * @param c - Morpho channel to communicate with Morpho VM
     */
    public MainWin(Channel c) {
        getIcon();
        initComponents();

        if (DebugWindow.debugMode()) {
            debug = new DebugWindow();
            debug.setVisible(true);
        }
        
        ed = new GeneralTextEditor(this, this);

        prefs = Preferences.userNodeForPackage(MainWin.class);
        
        getPref();
        
        setGameSpeed();
        setupMorpho(c);
        setupFC();
        
        setupGameController(rendererPanel);
        setupStepController();
        currentState = -1;
        state0();
        loadDefaults();
    }
    
    /**
     * Load default map and code
     */
    private void loadDefaults() {
        try {
            GameController.setGameContainer(MapIO.loadGame(MainWin.class.getResourceAsStream("/default.kbord")));
        } catch (Exception ex) {
            Logger.getLogger(MainWin.class.getName()).log(Level.SEVERE, null, ex);
        }
        defaultmap = true;
        try {
            textEditor.setText(TextIO.loadFile(MainWin.class.getResourceAsStream("/default.kforrit")));
        } catch (Exception ex) {
            Logger.getLogger(MainWin.class.getName()).log(Level.SEVERE, null, ex);
        }
        code = null;
    }
    
    /**
     * Initalize jFileChooser for loading maps.
     */
    private void setupFC() {
        fc.setFileSelectionMode(JFileChooser.FILES_ONLY); 
        fc.removeChoosableFileFilter(fc.getFileFilter());
        FileNameExtensionFilter kbord = new FileNameExtensionFilter("Karel borð (.kbord)", "kbord");
        fc.addChoosableFileFilter(kbord);
    }

    /**
     * Save window position, width, height and other variables.
     */
    private void setPref() {
        prefs.putInt(KEY_MAINWIN_WIDTH, getWidth());
        prefs.putInt(KEY_MAINWIN_HEIGHT, getHeight());
        prefs.putInt(KEY_MAINWIN_X, getX());
        prefs.putInt(KEY_MAINWIN_Y, getY());
        prefs.putInt(KEY_MAINWIN_VIEWSPLITTER, viewSplitter.getDividerLocation());
        prefs.putInt(KEY_MAINWIN_SPLITPANEL, splitPanel.getDividerLocation());
        prefs.putInt(KEY_MAINWIN_GAMESPEED, speedSlider.getValue());
    }

    /**
     * Load window position, width, height and other variables.
     */
    private void getPref() {
        speedSlider.setValue(prefs.getInt(KEY_MAINWIN_GAMESPEED, speedSlider.getValue()));
        splitPanel.setDividerLocation(prefs.getInt(KEY_MAINWIN_SPLITPANEL, splitPanel.getDividerLocation()));
        viewSplitter.setDividerLocation(prefs.getInt(KEY_MAINWIN_VIEWSPLITTER, viewSplitter.getDividerLocation()));
        setSize(prefs.getInt(KEY_MAINWIN_WIDTH, getWidth()), prefs.getInt(KEY_MAINWIN_HEIGHT, getHeight()));
        setLocation(prefs.getInt(KEY_MAINWIN_X, getX()), prefs.getInt(KEY_MAINWIN_Y, getY()));
        fc.setCurrentDirectory(new File(System.getProperty("user.dir")));
    }
    
    /**
     * Load the Icon for the JFrame.
     */
    private void getIcon() {
        try {
            setIconImage(ImageIO.read(getClass().getResourceAsStream("/images/icon.png")));
        } catch (IOException ex) {
            Logger.getLogger(MainWin.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Set the game speed according to speedSlider value.
     */
    private void setGameSpeed() {
        GameContainer.gameAnimationSpeed = GameContainer.maxAnimationSpeed - speedSlider.getValue() + 1;
    }

    /**
     * Initialize game controller with new renderer.
     * @param rndPanel - Panel to put new renderer on.
     */
    private void setupGameController(JPanel rndPanel) {
        BufferedRenderer r = new BufferedRenderer();
        rndPanel.add(r);
        GameController.setRenderer(r);
    }

    /**
     * Initialize step controller.
     */
    private void setupStepController() {
        StepController.setTextEditor(textEditor);
    }

    /**
     * Loads map form file and displays it on the renderer.
     * @param file - File to loead 
     */
    private void loadMap(File file) {
        try {
            map = file;
            GameController.setGameContainer(MapIO.loadGame(file));
            defaultmap = false;
        } catch (Exception ex) {
            GameController.setGameContainer(null);
            Console.clearConsole();
            Console.error("Gat ekki opnað kort: " + ex.getMessage());
            map = null;
        } 
    }

    /**
     * Parse program that is in the text editor.
     * @return - Assembly for the parsed program.
     */
    private String parseProgram() {
        Console.clearConsole();
        Reader r = textEditor.getReader();
        RDParserWithBaseCode yyparser = new RDParserWithBaseCode(r);
        try {
            yyparser.parse();
            if (DebugWindow.debugMode()) {
                DebugWindow.clearWindow();
                DebugWindow.print(yyparser.getDebugProgram());
            }
        } catch (java.io.IOException e) {
            Console.error("Forritið er með villur:");
            if (e instanceof RDParserException) {
                Console.error("Lína " + (((RDParserException) e).line + 1) + ": " + e.getMessage());
                textEditor.highlightError((RDParserException) e);
            } else {
                Console.error("IOException");
            }
            return null;
        }
        return yyparser.getProgram();
    }

    /**
     * Calls parseProgram() first and if it parsed successfully it runs it.
     * @param withStepping - Denotes if program should be stepping or not.
     * @return - true if operation successful, else false
     */
    private boolean parseAndRunProgram(boolean withStepping) {
        if (GameController.hasGameContainer()) {
            String program = parseProgram();
            if (program != null) { //Program compiled successfully
                StepController.isStepping = withStepping;
                GameController.startCommandThread();
                StepController.startStepThread();
                execute(program);
                textEditor.setEditable(false);
                return true;
            }
        } else {
            Console.clearConsole();
            Console.error("Það má ekki keyra forrit þegar ekkert borð er opið.");
        }
        return false;
    }

    /**
     * Cleanup code when programs stop execution.
     */
    private void programFinished() {
        StepController.isStepping = false;
        GameController.killCommandThread();
        StepController.killStepThread();
        textEditor.setEditable(true);
    }

    /**
     * Forces a program to stop execution.
     */
    private void forceStopProgram() {
        StepController.stopProgram();
        programFinished();
    }

    /**
     * Stops stepping on program and kicks it to run normally.
     */
    private void stopStepping() {
        textEditor.removeHighlights();
        StepController.isStepping = false; //Stop stepping
        StepController.kick(); //Let the program run
    }

    /**
     * Resets environment.
     */
    private void resetGame() {
        textEditor.removeHighlights();
        GameController.resetGameContainer();
    }

    /**
     * Sets the four control buttons visible (or not) according to parameters.
     * @param run
     * @param step
     * @param stop
     * @param reset 
     */
    private void setButtonsState(boolean run, boolean step, boolean stop, boolean reset) {
        runButton.setEnabled(run);
        stepButton.setEnabled(step);
        stopButton.setEnabled(stop);
        resetButton.setEnabled(reset);
    }
    
    public void getFocus() {
        this.repaint();
    }
    
    /**
     * Creates new file for text editor.
     * @return 
     */
    @Override
    public boolean newConcreteFile() {
        code = null;
        textEditor.setText("");
        return true;
    }

    /**
     * Save file in text editor.
     * @param file
     * @return 
     */
    @Override
    public boolean saveConcreteFile(File file) {
        try {
            TextIO.saveFile(file, textEditor.getText());
        } catch (Exception ex) {
            Logger.getLogger(MainWin.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
        return true;
    }

    /**
     * Loads file to text editor.
     * @param file
     * @return - true if successful, else false
     */
    @Override
    public boolean loadConcreteFile(File file) {
        try {
            textEditor.setText(TextIO.loadFile(file));
            code = file;
        } catch (Exception ex) {
            Logger.getLogger(MainWin.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
        return true;
    }

    /**
     * Executes cleanup code on system exit.
     */
    @Override
    public void exit() {
        setPref();
        if (DebugWindow.debugMode()) {
            debug.onClose();
        }
        setVisible(false);
        try {
            channel.close();
        } catch (Exception e) {
        }
        System.exit(0);
    }

    /**
     * Shows if all conditions are met to save file in text editor.
     * @return 
     */
    @Override
    public boolean isSavingAllowed() {
        return true;
    }

    // <editor-fold defaultstate="collapsed" desc="StateMachine">
    
    /*
     * This code is for controlling the buttons in the program. To understand
     * the state transitions look at state machine in Karel manual.
     */
    
    /**
     * Buttons 0 = Run, 1 = Step, 2 = Stop, 3 = Reset, 4 = Called when the 
     * program finishes normally
     *
     * @param button
     */
    private void changeState(int button) {
        if (button == 0 && currentState == 0) {
            stateTrans_0to1();
        } else if (button == 0 && currentState == 3) {
            stateTrans_3to1();
        } else if (button == 1 && currentState == 0) {
            stateTrans_0to3();
        } else if (button == 1 && currentState == 3) {
            stateTrans_3to3();
        } else if (button == 1 && currentState == 1) {
            stateTrans_1to3();
        } else if (button == 2 && currentState == 1) {
            stateTrans_1to2(true);
        } else if (button == 2 && currentState == 3) {
            stateTrans_3to2();
        } else if (button == 3 && currentState == 2) {
            stateTrans_2to0();
        } else if (button == 4 && (currentState == 1 || currentState == 3)) {
            stateTrans_1to2(false);
        }
    }

    private void stateTrans_0to1() { //Done
        //Play pressed in State0
        if (parseAndRunProgram(false)) { //Parse and execute program without stepping
            state1(); //Move to State 1
        }
    }

    private void stateTrans_0to3() { //Done
        //Step pressed in State0
        if (parseAndRunProgram(true)) { //Parse and execute program with stepping
            state3(); //Move to State 3
        }
    }
    
    private void stateTrans_1to2(boolean forcedStop) { //Done
        if (forcedStop) {
            //Stop pressed during execution in State1
            forceStopProgram(); //Stop the program
        } else {
            //Program finished by it self ...
            if (StepController.isStepping) {
                // ... in State3
                StepController.isStepping = false;
                textEditor.removeHighlights();
            }
            // ... in State1
            programFinished();
        }
        state2(); //Move to State 2
    }
    
    private void stateTrans_1to3() {
        StepController.isStepping = true;
        state3();
    }

    private void stateTrans_2to0() { //Done
        //Program is finished, reset button pressed
        resetGame(); //Reset map
        state0(); //Move to State 0
    }

    private void stateTrans_3to1() { //Done
        //Play button pressed during stepping
        stopStepping(); //Stop stepping
        state1(); //Move to State 1
    }

    private void stateTrans_3to2() { //Done
        //Stop button pressed during stepping
        stopStepping(); //Stop stepping
        forceStopProgram(); //Stop Program
        state2(); //Move to State 1
    }

    private void stateTrans_3to3() { //Done
        //Step button pressed during stepping
        StepController.kick(); //Take next step
        //state3(); //No need to transition states
    }

    /**
     * State #0 - Game is not running, only play and step clickable
     */
    private void state0() {
        currentState = 0;
        setButtonsState(true, true, false, false);
    }

    /**
     * State #1 - Game is running, only stop clickable
     */
    private void state1() {
        currentState = 1;
        setButtonsState(false, true, true, false);
    }

    /**
     * State #2 - Game not running, only refresh button clickable
     */
    private void state2() {
        currentState = 2;
        setButtonsState(false, false, false, true);
    }

    /**
     * State #3 - Game running in stepping mode
     */
    private void state3() {
        currentState = 3;
        setButtonsState(true, true, true, false);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Morpho communication">
    
    /**
     * Sets up communication with Morpho VM on channel c
     * @param c 
     */
    private void setupMorpho(Channel c) {
        channel = c;
        kicker =
                new Thread() {
                    @Override
                    public void run() {
                        for (;;) {
                            try {
                                sleep(100);
                            } catch (Exception e) {
                            }
                            kick();
                        }
                    }
                };
        kicker.setDaemon(true);
        kicker.start();
    }

    /**
     * Executes the program prog on the Morpho VM.
     * @param prog 
     */
    private void execute(String prog) {
        try {
            if (!channel.canWrite()) {
                return;
            }
            channel.write(prog);
        } catch (Exception e) {
        }
    }

    /**
     * This function is call from the Morpho VM to write program output to
     * the string buffer 'theOutput'.
     * @param obj 
     */
    public synchronized void write(Object obj) {
        String str = null;
        if (obj instanceof String) {
            str = (String) obj;
        } else if (obj != null) {
            str = obj.toString();
        }
        if (str == null) {
            flush();
        } else {
            theOutput.append(str);
        }
    }

    /**
     * Flushes what the string buffer 'theOutput' has to the console. 
     */
    public synchronized void flush() {
        lastFlush = System.nanoTime();
        if (theOutput.length() == 0) {
            return;
        }
        final String s = theOutput.toString();
        theOutput = new StringBuffer();
        java.awt.EventQueue.invokeLater(
                new Runnable() {
                    String str = s;

                    @Override
                    public void run() {
                        Console.print(str);
                    }
                });
    }

    /**
     * Controls frequency of flushing.
     */
    public synchronized void kick() {
        if (lastFlush + 1000000000 < System.nanoTime()) {
            return;
        }
        flush();
    }
    
    /**
     * Called from the Morpho VM when program starts and stops.
     * @param w 
     */
    public void setWaiting(boolean w) {
        if (w) {
            java.awt.EventQueue.invokeLater(
                    new Runnable() {
                        @Override
                        public void run() {
                        }
                    });
        } else {
            java.awt.EventQueue.invokeLater(
                    new Runnable() {
                        @Override
                        public void run() {
                            changeState(4);
                        }
                    });
        }
    }
    // </editor-fold>

    /*
     * Here below is auto generated code from NetBeans IDE 7.2.1
     */
    
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        viewSplitter = new javax.swing.JSplitPane();
        mainView = new javax.swing.JPanel();
        splitPanel = new javax.swing.JSplitPane();
        textPanel = new javax.swing.JPanel();
        buttonPanel = new javax.swing.JPanel();
        runButton = new javax.swing.JButton();
        resetButton = new javax.swing.JButton();
        speedSlider = new javax.swing.JSlider();
        speedLabel = new javax.swing.JLabel();
        stopButton = new javax.swing.JButton();
        stepButton = new javax.swing.JButton();
        textEditor = new karel.editors.texteditor.TextEditor();
        rendererPanel = new javax.swing.JPanel();
        consoleView = new javax.swing.JPanel();
        console = new karel.UI.Console();
        menuBar = new javax.swing.JMenuBar();
        menuFile = new javax.swing.JMenu();
        menuFileNew = new javax.swing.JMenuItem();
        jSeparator1 = new javax.swing.JPopupMenu.Separator();
        menuFileOpen = new javax.swing.JMenuItem();
        menuFileSave = new javax.swing.JMenuItem();
        menuFileSaveAs = new javax.swing.JMenuItem();
        menuFileSep1 = new javax.swing.JPopupMenu.Separator();
        menuFileExit = new javax.swing.JMenuItem();
        menuMap = new javax.swing.JMenu();
        menuMapLoadMap = new javax.swing.JMenuItem();
        jSeparator2 = new javax.swing.JPopupMenu.Separator();
        menuMapOpenEditor = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setTitle("Róbótinn Karel");
        setPreferredSize(new java.awt.Dimension(800, 600));
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        viewSplitter.setDividerLocation(450);
        viewSplitter.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        mainView.setLayout(new java.awt.BorderLayout());

        splitPanel.setDividerLocation(400);

        textPanel.setLayout(new java.awt.BorderLayout());

        buttonPanel.setLayout(new java.awt.GridBagLayout());

        runButton.setFont(karel.util.FontLoader.getDefaultFont(karel.util.FontLoader.ButtonFontType,karel.util.FontLoader.ButtonFontSize));
        runButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/Play.png"))); // NOI18N
        runButton.setText("Keyra");
        runButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        runButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        runButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                runButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 0.25;
        buttonPanel.add(runButton, gridBagConstraints);

        resetButton.setFont(karel.util.FontLoader.getDefaultFont(karel.util.FontLoader.ButtonFontType,karel.util.FontLoader.ButtonFontSize));
        resetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/Reset.png"))); // NOI18N
        resetButton.setText("Endursetja");
        resetButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        resetButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        resetButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                resetButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 0.25;
        buttonPanel.add(resetButton, gridBagConstraints);

        speedSlider.setMajorTickSpacing((int)GameContainer.maxAnimationSpeed/6);
        speedSlider.setMaximum((int)GameContainer.maxAnimationSpeed);
        speedSlider.setPaintTicks(true);
        speedSlider.setToolTipText("");
        speedSlider.setMinimumSize(new java.awt.Dimension(50, 32));
        speedSlider.setPreferredSize(null);
        speedSlider.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                speedSliderStateChanged(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 0.5;
        buttonPanel.add(speedSlider, gridBagConstraints);

        speedLabel.setFont(karel.util.FontLoader.getDefaultFont(karel.util.FontLoader.ButtonFontType,karel.util.FontLoader.ButtonFontSize));
        speedLabel.setText("Hraði Karel");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 4;
        buttonPanel.add(speedLabel, gridBagConstraints);

        stopButton.setFont(karel.util.FontLoader.getDefaultFont(karel.util.FontLoader.ButtonFontType,karel.util.FontLoader.ButtonFontSize));
        stopButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/Stop.png"))); // NOI18N
        stopButton.setText("Stoppa");
        stopButton.setEnabled(false);
        stopButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        stopButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        stopButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stopButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 0.25;
        buttonPanel.add(stopButton, gridBagConstraints);

        stepButton.setFont(karel.util.FontLoader.getDefaultFont(karel.util.FontLoader.ButtonFontType,karel.util.FontLoader.ButtonFontSize));
        stepButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/images/Step.png"))); // NOI18N
        stepButton.setText("Taka Skref");
        stepButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        stepButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        stepButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stepButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 0.25;
        buttonPanel.add(stepButton, gridBagConstraints);

        textPanel.add(buttonPanel, java.awt.BorderLayout.SOUTH);
        textPanel.add(textEditor, java.awt.BorderLayout.CENTER);

        splitPanel.setLeftComponent(textPanel);

        rendererPanel.setLayout(new java.awt.BorderLayout());
        splitPanel.setRightComponent(rendererPanel);

        mainView.add(splitPanel, java.awt.BorderLayout.CENTER);

        viewSplitter.setTopComponent(mainView);

        consoleView.setLayout(new java.awt.BorderLayout());
        consoleView.add(console, java.awt.BorderLayout.CENTER);

        viewSplitter.setBottomComponent(consoleView);

        getContentPane().add(viewSplitter, java.awt.BorderLayout.CENTER);

        menuFile.setText("Skrá");

        menuFileNew.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_N, java.awt.event.InputEvent.CTRL_MASK));
        menuFileNew.setIcon(new javax.swing.ImageIcon(getClass().getResource("/common/new.png"))); // NOI18N
        menuFileNew.setText("Nýtt forrit");
        menuFileNew.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                menuFileNewActionPerformed(evt);
            }
        });
        menuFile.add(menuFileNew);
        menuFile.add(jSeparator1);

        menuFileOpen.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        menuFileOpen.setIcon(new javax.swing.ImageIcon(getClass().getResource("/common/open.png"))); // NOI18N
        menuFileOpen.setText("Opna forrit...");
        menuFileOpen.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                menuFileOpenActionPerformed(evt);
            }
        });
        menuFile.add(menuFileOpen);

        menuFileSave.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        menuFileSave.setIcon(new javax.swing.ImageIcon(getClass().getResource("/common/save.png"))); // NOI18N
        menuFileSave.setText("Vista forrit");
        menuFileSave.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                menuFileSaveActionPerformed(evt);
            }
        });
        menuFile.add(menuFileSave);

        menuFileSaveAs.setIcon(new javax.swing.ImageIcon(getClass().getResource("/common/saveas.png"))); // NOI18N
        menuFileSaveAs.setText("Vista forrit sem...");
        menuFileSaveAs.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                menuFileSaveAsActionPerformed(evt);
            }
        });
        menuFile.add(menuFileSaveAs);
        menuFile.add(menuFileSep1);

        menuFileExit.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.CTRL_MASK));
        menuFileExit.setIcon(new javax.swing.ImageIcon(getClass().getResource("/common/exit.png"))); // NOI18N
        menuFileExit.setText("Loka Karel");
        menuFileExit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                menuFileExitActionPerformed(evt);
            }
        });
        menuFile.add(menuFileExit);

        menuBar.add(menuFile);

        menuMap.setText("Borð");

        menuMapLoadMap.setIcon(new javax.swing.ImageIcon(getClass().getResource("/common/open.png"))); // NOI18N
        menuMapLoadMap.setText("Opna Borð");
        menuMapLoadMap.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                menuMapLoadMapActionPerformed(evt);
            }
        });
        menuMap.add(menuMapLoadMap);
        menuMap.add(jSeparator2);

        menuMapOpenEditor.setIcon(new javax.swing.ImageIcon(getClass().getResource("/editor/smallEditIco.png"))); // NOI18N
        menuMapOpenEditor.setText("Borðvinnsla");
        menuMapOpenEditor.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                menuMapOpenEditorActionPerformed(evt);
            }
        });
        menuMap.add(menuMapOpenEditor);

        menuBar.add(menuMap);

        setJMenuBar(menuBar);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runButtonActionPerformed
        changeState(0);
    }//GEN-LAST:event_runButtonActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        ed.exit();
    }//GEN-LAST:event_formWindowClosing

    private void menuMapLoadMapActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuMapLoadMapActionPerformed
        int returnVal = fc.showOpenDialog(this);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            loadMap(fc.getSelectedFile());
        }
    }//GEN-LAST:event_menuMapLoadMapActionPerformed

    private void resetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetButtonActionPerformed
        changeState(3);
    }//GEN-LAST:event_resetButtonActionPerformed

    private void speedSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_speedSliderStateChanged
        setGameSpeed();
    }//GEN-LAST:event_speedSliderStateChanged

    private void menuFileExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuFileExitActionPerformed
        ed.exit();
    }//GEN-LAST:event_menuFileExitActionPerformed

    private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopButtonActionPerformed
        changeState(2);
    }//GEN-LAST:event_stopButtonActionPerformed

    private void stepButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stepButtonActionPerformed
        changeState(1);
    }//GEN-LAST:event_stepButtonActionPerformed

    private void menuFileOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuFileOpenActionPerformed
        ed.load();
    }//GEN-LAST:event_menuFileOpenActionPerformed

    private void menuFileSaveAsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuFileSaveAsActionPerformed
        ed.saveAs();
    }//GEN-LAST:event_menuFileSaveAsActionPerformed

    private void menuFileNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuFileNewActionPerformed
        ed.newFile();
    }//GEN-LAST:event_menuFileNewActionPerformed

    private void menuFileSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuFileSaveActionPerformed
        ed.save();
    }//GEN-LAST:event_menuFileSaveActionPerformed

    private void menuMapOpenEditorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuMapOpenEditorActionPerformed
        if(mapEd == null || !mapEd.isVisible()) {
            
            if(defaultmap || (map == null && GameController.getGameContainer() == null)) {
                mapEd = new MapEditor();
            } else if(map == null) {
                mapEd = new MapEditor(GameController.getGameContainer());
            } else {
                mapEd = new MapEditor(map);
            }
            mapEd.setMainWin(this);
            mapEd.setVisible(true);
        } else {
                mapEd.toFront();
                mapEd.repaint();
        }
    }//GEN-LAST:event_menuMapOpenEditorActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel buttonPanel;
    private karel.UI.Console console;
    private javax.swing.JPanel consoleView;
    private javax.swing.JPopupMenu.Separator jSeparator1;
    private javax.swing.JPopupMenu.Separator jSeparator2;
    private javax.swing.JPanel mainView;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JMenu menuFile;
    private javax.swing.JMenuItem menuFileExit;
    private javax.swing.JMenuItem menuFileNew;
    private javax.swing.JMenuItem menuFileOpen;
    private javax.swing.JMenuItem menuFileSave;
    private javax.swing.JMenuItem menuFileSaveAs;
    private javax.swing.JPopupMenu.Separator menuFileSep1;
    private javax.swing.JMenu menuMap;
    private javax.swing.JMenuItem menuMapLoadMap;
    private javax.swing.JMenuItem menuMapOpenEditor;
    private javax.swing.JPanel rendererPanel;
    private javax.swing.JButton resetButton;
    private javax.swing.JButton runButton;
    private javax.swing.JLabel speedLabel;
    private javax.swing.JSlider speedSlider;
    private javax.swing.JSplitPane splitPanel;
    private javax.swing.JButton stepButton;
    private javax.swing.JButton stopButton;
    private karel.editors.texteditor.TextEditor textEditor;
    private javax.swing.JPanel textPanel;
    private javax.swing.JSplitPane viewSplitter;
    // End of variables declaration//GEN-END:variables
}
